/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Mixin with profile-related helpers (for people and groups)
 */

import Log from './log.js';

const {ensureTimestamp} = require('./utils');

var mergeModifiers = function mergeModifiers(data, modifiers) {
  if (modifiers) {
    if (modifiers.$ignoreAlias) {
      data.$ignoreAlias = modifiers.$ignoreAlias;
    }
    if (modifiers.$ignoreTime) {
      data.$ignoreTime = modifiers.$ignoreTime;
    }
    if (Object.prototype.hasOwnProperty.call(modifiers, '$ip')) {
      data.$ip = modifiers.$ip;
    }
    if (Object.prototype.hasOwnProperty.call(modifiers, '$time')) {
      data.$time = ensureTimestamp(modifiers.$time);
    }
    if (Object.prototype.hasOwnProperty.call(modifiers, '$latitude') &&
      Object.prototype.hasOwnProperty.call(modifiers, '$longitude')) {
      data.$latitude = modifiers.$latitude;
      data.$longitude = modifiers.$longitude;
    }
  }
  return data;
};

var ProfileHelpers = (Base = Object) => class extends Base {
  get token() {
    return this.mixpanel.token;
  }

  get config() {
    return this.mixpanel.config;
  }

  _set(prop, to, modifiers, callback, {identifiers, setOnce = false}) {
    let $set = {};

    if (typeof (prop) === 'object') {
      if (typeof (to) === 'object') {
        callback = modifiers;
        modifiers = to;
      } else {
        callback = to;
      }
      $set = prop;
    } else {
      $set[prop] = to;
      if (typeof (modifiers) === 'function' || !modifiers) {
        callback = modifiers;
      }
    }

    let data = {
      '$token': this.token,
      ...identifiers,
    };

    const setKey = setOnce ? '$setOnce' : '$set';
    data[setKey] = $set;

    if ('ip' in $set) {
      data.$ip = $set.ip;
      delete $set.ip;
    }

    if ($set.$ignoreTime) {
      data.$ignoreTime = $set.$ignoreTime;
      delete $set.$ignoreTime;
    }

    data = mergeModifiers(data, modifiers);

    if (this.config.debug) {
      Log.showInfo(`Sending the following data to Mixpanel (${this.endpoint}):`);
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: this.endpoint, data }, callback);
  }

  _deleteProfile({identifiers, modifiers, callback}) {
    let data = {
      '$delete': '',
      '$token': this.token,
      ...identifiers,
    };

    if (typeof (modifiers) === 'function') {
      callback = modifiers;
    }

    data = mergeModifiers(data, modifiers);

    if (this.config.debug) {
      Log.showInfo(`Deleting profile ${JSON.stringify(identifiers)}`);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: this.endpoint, data }, callback);
  }

  _remove({identifiers, data, modifiers, callback}) {
    let $remove = {};

    if (typeof (data) !== 'object' || Array.isArray(data)) {
      if (this.config.debug) {
        Log.showError('Invalid value passed to #remove - data must be an object with scalar values');
      }
      return;
    }

    for (const key in data) {
      if (Object.prototype.hasOwnProperty.call(data, key)) {
        const val = data[key];
        if (typeof (val) === 'string' || typeof (val) === 'number') {
          $remove[key] = val;
        } else {
          if (this.config.debug) {
            Log.showError('Invalid argument passed to #remove - values must be scalar');
            Log.showError('Passed ' + key + ':', val);
          }
          return;
        }
      }
    }

    if (Object.keys($remove).length === 0) {
      return;
    }

    data = {
      '$remove': $remove,
      '$token': this.token,
      ...identifiers
    };

    if (typeof (modifiers) === 'function') {
      callback = modifiers;
    }

    data = mergeModifiers(data, modifiers);

    if (this.config.debug) {
      Log.showInfo(`Sending the following data to Mixpanel (${this.endpoint}):`);
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: this.endpoint, data }, callback);
  }

  _union({identifiers, data, modifiers, callback}) {
    let $union = {};

    if (typeof (data) !== 'object' || Array.isArray(data)) {
      if (this.config.debug) {
        Log.showError('Invalid value passed to #union - data must be an object with scalar or array values');
      }
      return;
    }

    for (const key in data) {
      if (Object.prototype.hasOwnProperty.call(data, key)) {
        const val = data[key];
        if (Array.isArray(val)) {
          var mergeValues = val.filter(function (v) {
            return typeof (v) === 'string' || typeof (v) === 'number';
          });
          if (mergeValues.length > 0) {
            $union[key] = mergeValues;
          }
        } else if (typeof (val) === 'string' || typeof (val) === 'number') {
          $union[key] = [val];
        } else {
          if (this.config.debug) {
            Log.showError('Invalid argument passed to #union - values must be a scalar value or array');
            Log.showError('Passed ' + key + ':', val);
          }
        }
      }
    }

    if (Object.keys($union).length === 0) {
      return;
    }

    data = {
      '$union': $union,
      '$token': this.token,
      ...identifiers,
    };

    if (typeof (modifiers) === 'function') {
      callback = modifiers;
    }

    data = mergeModifiers(data, modifiers);

    if (this.config.debug) {
      Log.showInfo(`Sending the following data to Mixpanel (${this.endpoint}):`);
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: this.endpoint, data }, callback);
  }

  _unset({identifiers, prop, modifiers, callback}) {
    let $unset = [];

    if (Array.isArray(prop)) {
      $unset = prop;
    } else if (typeof (prop) === 'string') {
      $unset = [prop];
    } else {
      if (this.config.debug) {
        Log.showError('Invalid argument passed to #unset - must be a string or array');
        Log.showError('Passed: ' + prop);
      }
      return;
    }

    let data = {
      '$unset': $unset,
      '$token': this.token,
      ...identifiers,
    };

    if (typeof (modifiers) === 'function') {
      callback = modifiers;
    }

    data = mergeModifiers(data, modifiers);

    if (this.config.debug) {
      Log.showInfo(`Sending the following data to Mixpanel (${this.endpoint}):`);
      Log.showInfo(data);
    }

    this.mixpanel.sendRequest({ method: 'GET', endpoint: this.endpoint, data }, callback);
  }
};

export {ProfileHelpers, mergeModifiers}
